package edu.pku.sei.mte.runtime;

import java.util.HashMap;
import java.util.Map;

import edu.pku.sei.mte.mtemodel.core.DataType;
import edu.pku.sei.mte.mtemodel.core.NamedElement;
import edu.pku.sei.mte.mtemodel.graph.Graph;
import edu.pku.sei.mte.mtemodel.graph.GraphObject;
import edu.pku.sei.mte.mtemodel.mtmodel.common.ModelVariable;
import edu.pku.sei.mte.mtemodel.mtmodel.common.Variable;
import edu.pku.sei.mte.mtemodel.mtmodel.pattern.TPatternNode;

public class Context implements Cloneable{
	private Map<String,Variable> variables = null;
	private Context parentContext = null;
	private Environment environment;
	
	public Map<String, Variable> getVariables() {
		if(variables==null)
			variables = new HashMap<String,Variable>();
		return variables;
	}
	
	public Variable getVariable(String name){
		return getVariables().get(name);
	}
	
	/**
	 * call Variable.setValue() to finish the operation
	 * @param name
	 * @param value
	 */
	public void setVariable(String name,Object value){
		Variable v = getVariables().get(name);
		if(v==null){
			System.out.println("Context/setVariable: You want to set a variable named "+name+", but it doesn't exist in the context");
			return;
		}
		v.setValue(value);
	}
	
	public Variable registVariable(String name,DataType type){
		Variable v = null;
		v = new Variable();
		v.setName(name);
		v.setType(type);
		getVariables().put(name, v);
		return v;
	}
	
	public Variable registModelVariable(NamedElement node,DataType type){
		ModelVariable v = null;
		v = new ModelVariable();
		v.setName(node.getName());
		v.setType(type);
		v.setPatternElement(node);
		getVariables().put(node.getName(), v);
		return v;
	}

	@Override
	public Object clone() {
		Context c = null;
		try {
			c = (Context) super.clone();
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if(c != null){
			c.variables = new HashMap<String,Variable>();
			for(Variable v : getVariables().values()){
				c.variables.put(v.getName(), (Variable) v.clone());
			}
		}
		return c;
	}

	@SuppressWarnings("unchecked")
	public boolean checkModelBindingExistIn(Object value, Graph pattern) {
		Map<String,Variable> m = getVariables();
		for(Variable v : m.values()){
			if(v instanceof ModelVariable){
				ModelVariable mv = (ModelVariable)v;
				if(((GraphObject)mv.getPatternElement()).getHost()==pattern){
					if(v.getValue()==value)
						return true;
				}
			}
		}
		return false;
	}
	
	@Deprecated
	@SuppressWarnings("unchecked")
	public boolean checkModelBindingExist(TPatternNode node,Object value){
		Map<String,Variable> m = getVariables();
		for(Variable v : m.values()){
			if(v instanceof ModelVariable){
				ModelVariable mv = (ModelVariable)v;
				if(((GraphObject)mv.getPatternElement()).getHost()==node.getHost()){
					if(v.getValue()==value&&node.getName().equals(v.getName())==false)
						return true;
				}
			}
		}
		return false;
	}
	
	@SuppressWarnings("unchecked")
	public boolean isValid(){
		Map<String,Variable> m = getVariables();
		for(Variable v : m.values()){
			Object value = v.getValue();
			if (value != null && value instanceof GraphObject)
				if (((GraphObject) value).getHost() == null)// deleted
					return false;

		}
		return true;
	}
	
	@Override
	public boolean equals(Object arg0) {
		if(arg0 == this)
			return true;
		if(arg0 instanceof Context == false)
			return false;
		Context c = (Context) arg0;
		for(Map.Entry<String,Variable> e : getVariables().entrySet()){
			Variable v1, v2;
			v1 = e.getValue();
			v2 = c.getVariable(e.getKey());
			if(v2==null)
				return false;
			if(v1.getValue().equals(v2.getValue())==false)
				return false;
		}
		return super.equals(arg0);
	}

	public Context getParentContext() {
		return parentContext;
	}

	public void setParentContext(Context parentContext) {
		this.parentContext = parentContext;
	}

	public Environment getEnvironment() {
		return environment;
	}

	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}
}
